package com.jdd.modules.parking.controller;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.jdd.common.api.vo.CloudParkConfigVo;
import com.jdd.common.api.vo.RestTemplateVo;
import com.jdd.common.api.vo.Result;
import com.jdd.common.api.vo.ResultVo;
import com.jdd.common.aspect.annotation.*;
import com.jdd.common.system.query.QueryGenerator;
import com.jdd.common.system.vo.LoginUser;
import com.jdd.common.util.GuavaRetryingUtils;
import com.jdd.common.util.LoginUserUtils;
import com.jdd.common.util.RestTemplateUtils;
import com.jdd.common.util.SnowFlakeUtil;
import com.jdd.common.util.excel.entity.params.ExportParams;
import com.jdd.common.util.excel.entity.params.ImportParams;
import com.jdd.common.util.excel.util.ExcelImportUtil;
import com.jdd.common.util.excel.web.entity.vo.NormalExcelConstants;
import com.jdd.common.util.excel.web.view.EasypoiSingleExcelView;
import com.jdd.modules.area.entity.ChargeRules;
import com.jdd.modules.area.entity.ChargeRulesModule;
import com.jdd.modules.area.entity.ParkAreaInfo;
import com.jdd.modules.area.service.IChargeRulesModuleService;
import com.jdd.modules.area.service.IChargeRulesService;
import com.jdd.modules.area.service.IParkAreaInfoService;
import com.jdd.modules.configcartype.entity.ConfigCarType;
import com.jdd.modules.configcartype.service.IConfigCarTypeService;
import com.jdd.modules.parking.entity.*;
import com.jdd.modules.parking.service.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @Description: 停车场信息
 * @Author: jdd
 * @Date: 2021-02-05
 * @Version: V1.0
 */
@Slf4j
@Api(tags = "停车场信息")
@RestController
@RequestMapping("/parking/parkInfo")
public class ParkInfoController {


    public String appScrect;




    public String appId;
    @Value("${parkInfo.parkCode}")
    public String parkCode;

    @Resource
    private RestTemplateUtils restTemplateUtils;
    @Resource
    IParkInfoService parkInfoService;
    @Resource
    IConfigParkChargeService configParkChargeService;
    @Resource
    IConfigParkInfoService configParkInfoService;
    @Resource
    IParkArmInfoService parkArmInfoService;
    @Resource
    IConfigParkThroughService parkThroughService;
    @Resource
    IConfigCarTypeService configCarTypeService;
    @Resource
    IparkCarEnterLogService parkCarEnterLogService;
    @Resource
    IParkAreaInfoService parkAreaInfoService;
    @Resource
    IChargeRulesService chargeRulesService;
    @Resource
    IChargeRulesModuleService chargeRulesModuleService;

    @Value("${parkInfo.parkCode}")
    private String parkCodeSyn;

    /**
     * 分页列表查询
     *
     * @param parkInfo
     * @param pageNo
     * @param pageSize
     * @param req
     * @return
     */
    @AutoLog(value = "停车场信息-分页列表查询")
    @ApiOperation(value = "停车场信息-分页列表查询", notes = "停车场信息-分页列表查询")
    @GetMapping(value = "/list")
    public Result<IPage<ParkInfo>> queryPageList(ParkInfo parkInfo,
                                                 @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
                                                 @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, String startTime, String endTime,
                                                 HttpServletRequest req) {
        Result<IPage<ParkInfo>> result = new Result<IPage<ParkInfo>>();
        QueryWrapper<ParkInfo> queryWrapper = QueryGenerator.initQueryWrapper(parkInfo, req.getParameterMap());
        Page<ParkInfo> page = new Page<ParkInfo>(pageNo, pageSize);

        if (StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime)) {
            queryWrapper.and(wrapper -> wrapper.ge("create_time", startTime + " 00:00:00").le("create_time", endTime + " 23:59:59"));
        }
        IPage<ParkInfo> pageList = parkInfoService.page(page, queryWrapper);
        result.setSuccess(true);
        result.setResult(pageList);
        return result;
    }


    @AutoLog(value = "停车场信息-分页列表查询")
    @ApiOperation(value = "停车场信息-分页列表查询", notes = "停车场信息-分页列表查询")
    @GetMapping(value = "/parkInfoList")
    public Result<List<ParkInfo>> parkInfoList() {
        Result<List<ParkInfo>> result = new Result<List<ParkInfo>>();
        List<ParkInfo> pageList = parkInfoService.list();
        result.setSuccess(true);
        result.setResult(pageList);
        return result;
    }

    /**
     * @Title: homePageParkList
     * @Description:根据登录用户绑定车场组查询车场信息
     * @return
     * @author: zjf
     * @date 2021年10月27日
     * @version 1.0
     */
    @AutoLog(value = "停车场信息")
    @ApiOperation(value = "停车场信息", notes = "停车场信息")
    @GetMapping(value = "/monthlyParkInfoList")
    public Result<?> monthlyParkInfoList() {
        List<ParkInfo> parkInfoList = parkInfoService.list(null);
        return Result.ok(parkInfoList);
    }
    /**
     * 添加
     *
     * @param parkInfo
     * @return
     */
    @AutoLog(value = "停车场信息-添加")
    @ApiOperation(value = "停车场信息-添加", notes = "停车场信息-添加")
    @PostMapping(value = "/add")
    @OperationLogDetail(detail = "停车场信息，添加", level = LogLevelType.ONE, operationType = OperationType.INSERT, operationUnit = OperationUnit.UNKNOWN)
    public Result<ParkInfo> add(@RequestBody ParkInfo parkInfo) {
        Result<ParkInfo> result = new Result<ParkInfo>();
        try {
            LoginUser sysUser =
                    (LoginUser) SecurityUtils.getSubject().getPrincipal();
            String name = sysUser.getUsername();
            parkInfo.setCreateUser(name);
            String parkCode = "";
            boolean flag = true;
            QueryWrapper<ParkInfo> wrapper = new QueryWrapper<ParkInfo>();
            while (flag) {
                parkCode = getParkCode();
                wrapper.eq("park_code", parkCode);
                List<ParkInfo> list = parkInfoService.list(wrapper);
                if (list.size() == 0) {
                    flag = false;
                }
            }
            parkInfo.setParkCode(parkCode);
            parkInfo.setId(SnowFlakeUtil.getId());
            parkInfoService.saveEntity(parkInfo);
            result.success("添加成功！");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            result.error500("操作失败");
        }
        return result;
    }

    /**
     * 编辑
     *
     * @param parkInfo
     * @return
     */
    @AutoLog(value = "停车场信息-编辑")
    @ApiOperation(value = "停车场信息-编辑", notes = "停车场信息-编辑")
    @PutMapping(value = "/edit")
    @OperationLogDetail(detail = "停车场信息，编辑", level = LogLevelType.ONE, operationType = OperationType.UPDATE, operationUnit = OperationUnit.UNKNOWN)
    public Result<ParkInfo> edit(@RequestBody ParkInfo parkInfo) {
        Result<ParkInfo> result = new Result<ParkInfo>();
        ParkInfo parkInfoEntity = parkInfoService.findParkInfoById(parkInfo.getId());
        if (parkInfoEntity == null) {
            result.error500("未找到对应实体");
        } else {
            boolean ok = parkInfoService.updateEntity(parkInfo);
            //TODO 返回false说明什么？
            if (ok) {
                result.success("修改成功!");
            }
        }
        return result;
    }

    /**
     * 从云端下载车场配置信息
     */
    @AutoLog(value = "停车场信息-同步云端配置信息")
    @ApiOperation(value = "停车场信息-同步云端配置信息", notes = "停车场信息-同步云端配置信息")
    @PostMapping(value = "/synchronizeConfiguration")
    @OperationLogDetail(detail = "停车场信息-同步云端配置信息", level = LogLevelType.ONE, operationType = OperationType.UPDATE, operationUnit = OperationUnit.UNKNOWN)
    public Result<?> synchronizeConfiguration() {
        RestTemplateVo restTemplateVo = new RestTemplateVo();
        restTemplateVo.setParkCode(parkCode);
        String url = "";
        ResultVo<?> body;
        try {
            body = GuavaRetryingUtils.restTemplatePosts(restTemplateVo, url, restTemplateUtils, appId, appScrect);
        } catch (Exception e) {
            log.error("获取云端数据失败，异常信息：{}", e.getMessage());
            return Result.error("获取云端数据失败");
        }
        CloudParkConfigVo cloudParkConfigVo;
        try {
            String dataStr = String.valueOf(body.getData());
            cloudParkConfigVo = JSONObject.parseObject(dataStr, CloudParkConfigVo.class);
        } catch (Exception e) {
            log.error("解析云端数据失败，异常信息：{}", e.getMessage());
            return Result.error("解析云端数据失败");
        }
        try {
            ParkInfo parkInfo = cloudParkConfigVo.getParkInfo();
            parkInfoService.removeAllInfo();
            parkInfoService.save(parkInfo);
            List<ConfigCarType> configCarTypes = cloudParkConfigVo.getConfigCarTypes();
            configCarTypeService.removeAllInfo();
            if(CollectionUtil.isNotEmpty(configCarTypes)){
                configCarTypeService.saveBatch(configCarTypes);
            }
            ConfigParkInfo configParkInfo = cloudParkConfigVo.getConfigParkInfo();
            configParkInfoService.removeAllInfo();
            if(null!=configParkInfo){
                configParkInfoService.save(configParkInfo);
            }
            List<ParkArmInfo> parkArmInfos = cloudParkConfigVo.getParkArmInfos();
            parkArmInfoService.removeAllInfo();
            for (ParkArmInfo parkArmInfo : parkArmInfos) {
                //根据相机类型获拼接频流rtsp地址
                parkArmInfo.setRtspUrl(parkArmInfoService.jointRtspUrl(parkArmInfo, configParkInfo));
                //根据相机类型拼接视频流video地址
                parkArmInfo.setVideoUrl(parkArmInfoService.jointVideoUrl(parkArmInfo, configParkInfo));
            }
            if(CollectionUtil.isNotEmpty(parkArmInfos)){
                parkArmInfoService.saveBatch(parkArmInfos);
            }
            List<ConfigParkCharge> configParkCharges = cloudParkConfigVo.getConfigParkCharges();
            configParkChargeService.removeAllInfo();
            if (CollectionUtil.isNotEmpty(configParkCharges)) {
                configParkChargeService.saveBatch(configParkCharges);
            }
            List<ConfigParkThrough> configParkThroughList = cloudParkConfigVo.getConfigParkThroughList();
            parkThroughService.removeAllInfo();
            if (CollectionUtil.isNotEmpty(configParkThroughList)) {
                parkThroughService.saveBatch(configParkThroughList);
            }
            List<ParkAreaInfo> parkAreaInfos = cloudParkConfigVo.getParkAreaInfos();
            parkAreaInfoService.removeAllInfo();
            if (CollectionUtil.isNotEmpty(parkAreaInfos)) {
                parkAreaInfoService.saveBatch(parkAreaInfos);
            }
            List<ChargeRules> chargeRules = cloudParkConfigVo.getChargeRules();
            chargeRulesService.removeAllInfo();
            if (CollectionUtil.isNotEmpty(chargeRules)) {
                chargeRulesService.saveBatch(chargeRules);
            }
            List<ChargeRulesModule> chargeRulesModules = cloudParkConfigVo.getChargeRulesModules();
            chargeRulesModuleService.removeAllInfo();
            if (CollectionUtil.isNotEmpty(chargeRulesModules)) {
                chargeRulesModuleService.saveBatch(chargeRulesModules);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("保存云端数据失败，异常信息：{}", e.getMessage());
            return Result.error("保存云端数据失败");
        }
        return Result.ok("同步云端数据成功");
    }


    /**
     * 编辑
     *
     * @param parkInfo
     * @return
     */
    @AutoLog(value = "停车场信息-编辑")
    @ApiOperation(value = "停车场信息-编辑", notes = "停车场信息-编辑")
    @PostMapping(value = "/editParkInfo")
    @OperationLogDetail(detail = "停车场信息，编辑", level = LogLevelType.ONE, operationType = OperationType.UPDATE, operationUnit = OperationUnit.UNKNOWN)
    public Result<ParkInfo> editParkInfo(@RequestBody ParkInfo parkInfo) {
        Result<ParkInfo> result = new Result<ParkInfo>();
        ParkInfo parkInfoEntity = parkInfoService.findParkInfoById(parkInfo.getId());
        if (parkInfoEntity == null) {
            result.error500("未找到对应实体");
        } else {
            boolean ok = parkInfoService.updateEntity(parkInfo);
            //TODO 返回false说明什么？
            if (ok) {
                result.success("切换成功!");
            }
        }

        return result;
    }

    /**
     * 批量删除
     *
     * @param ids
     * @return
     */
    @AutoLog(value = "停车场信息-批量删除")
    @ApiOperation(value = "停车场信息-批量删除", notes = "停车场信息-批量删除")
    @DeleteMapping(value = "/deleteBatch")
    @OperationLogDetail(detail = "停车场信息，批量删除", level = LogLevelType.ONE, operationType = OperationType.DELETE, operationUnit = OperationUnit.UNKNOWN)
    public Result<ParkInfo> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
        Result<ParkInfo> result = new Result<ParkInfo>();
        if (ids == null || "".equals(ids.trim())) {
            result.error500("参数不识别！");
        } else {
            this.parkInfoService.deleteEntityByIds(Arrays.asList(ids.split(",")));
            result.success("删除成功!");
        }
        return result;
    }

    /**
     * 通过id查询
     *
     * @param id
     * @return
     */
    @AutoLog(value = "停车场信息-通过id查询")
    @ApiOperation(value = "停车场信息-通过id查询", notes = "停车场信息-通过id查询")
    @GetMapping(value = "/queryById")
    public Result<ParkInfo> queryById(@RequestParam(name = "id", required = true) String id) {
        Result<ParkInfo> result = new Result<ParkInfo>();
        ParkInfo parkInfo = parkInfoService.findParkInfoById(id);
        if (parkInfo == null) {
            result.error500("未找到对应实体");
        } else {
            result.setResult(parkInfo);
            result.setSuccess(true);
        }
        return result;
    }

    /**
     * 导出excel
     *
     * @param request
     * @param response
     */
    @RequestMapping(value = "/exportXls")
    public ModelAndView exportXls(HttpServletRequest request, HttpServletResponse response, ParkInfo parkInfo) {
        // Step.1 组装查询条件
        QueryWrapper<ParkInfo> queryWrapper = QueryGenerator.initQueryWrapper(parkInfo, request.getParameterMap());
        //Step.2 AutoPoi 导出Excel
        ModelAndView mv = new ModelAndView(new EasypoiSingleExcelView());
        List<ParkInfo> pageList = parkInfoService.list(queryWrapper);
        //导出文件名称
        mv.addObject(NormalExcelConstants.FILE_NAME, "停车场信息列表");
        mv.addObject(NormalExcelConstants.CLASS, ParkInfo.class);
        mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("停车场信息列表数据", "导出信息"));
        mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
        return mv;
    }

    /**
     * 通过excel导入数据
     *
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
    public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
        for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
            // 获取上传文件对象
            MultipartFile file = entity.getValue();
            ImportParams params = ImportParams.builder().build();
            params.setTitleRows(2);
            params.setHeadRows(1);
            params.setNeedSave(true);
            try {
                List<ParkInfo> listParkInfos = ExcelImportUtil.importExcel(file.getInputStream(), ParkInfo.class, params);
                parkInfoService.saveBatch(listParkInfos);
                return Result.ok("文件导入成功！数据行数:" + listParkInfos.size());
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return Result.error("文件导入失败:" + e.getMessage());
            } finally {
                try {
                    file.getInputStream().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return Result.ok("文件导入失败！");
    }

    public String getParkCode() {
        //使用5位随机数表示车场park_code
        //String parkCode = RandomStringUtils.randomNumeric(5);
        return parkCodeSyn;
    }

    /**
     * 该方法是场端代码，用于组成向云端发送的场端数据库中的数据
     *
     * @param object  用于封装数据，将数据以Json格式传出
     * @param type    数据实体类类型
     * @param service 数据库调用接口，与type一起使用，来获取指定的数据库表信息
     **/
    private <T> void addParkConfigData(JSONObject object, Class<T> type, IService<T> service) {
        // 定义查询体
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        // 获取要查询的数据类型
        String typeName = type.getSimpleName();
        // 转化格式
        typeName = typeName.replaceFirst(typeName.charAt(0) + "", Character.toLowerCase(typeName.charAt(0)) + "");
        // 查询数据，并返回
        object.put(typeName + "List", service.list(queryWrapper));
    }

    /**
     * 该方法是向封装好的向云端发送的同步数据中添加车场的基础数据
     *
     * @param object 用于封装数据，将数据以Json格式传出
     **/
    private void addParkBaseSyncData(JSONObject object) {
        addParkConfigData(object, ParkInfo.class, parkInfoService);
        addParkConfigData(object, ParkArmInfo.class, parkArmInfoService);
        addParkConfigData(object, ConfigParkInfo.class, configParkInfoService);
        addParkConfigData(object, ConfigParkCharge.class, configParkChargeService);
        addParkConfigData(object, ConfigParkThrough.class, parkThroughService);
        addParkConfigData(object, ConfigCarType.class, configCarTypeService);
    }

    /**
     * 该方法是生成一个带有登录者信息的JsonObject
     **/
    private JSONObject loginUserObject() {
        JSONObject jsonObject = new JSONObject();
        LoginUser loginUser = LoginUserUtils.getLoginUser();
        jsonObject.put("loginUser", loginUser);
        return jsonObject;
    }


}
